#pragma once

#include"Sem.hpp"

#include<iostream>
#include<vector>

constexpr int _DEFAULT_CAPACITY = 5;

template<class T>
class RingQueue {
public:
  RingQueue(int capacity = _DEFAULT_CAPACITY)
    :_rq(capacity)
     ,_capacity(capacity)
     ,_conIndex(0)
     ,_proIndex(0)
     ,_proSem(capacity)
     ,_conSem(0)
  {
    pthread_mutex_init(&_proMutex, nullptr);
    pthread_mutex_init(&_conMutex, nullptr);
  }

  void Push(const T& data) {
    _proSem.P();

    pthread_mutex_lock(&_proMutex);

    _rq[_proIndex++] = data;
    _proIndex %= _capacity;

    pthread_mutex_unlock(&_proMutex);

    _conSem.V();
  }

  void Pop(T& data) {
    _conSem.P();

    pthread_mutex_lock(&_conMutex);

    data = _rq[_conIndex++];
    _conIndex %= _capacity;

    pthread_mutex_unlock(&_conMutex);
    _proSem.V();
  }

  ~RingQueue() {
    pthread_mutex_destroy(&_proMutex);
    pthread_mutex_destroy(&_conMutex);
  }
private:
  std::vector<T> _rq;
  
  int _capacity;
  // index with con
  int _conIndex;
  // index with pro
  int _proIndex;
  // Sem with consumer
  Sem _conSem;
  // Sem with productor
  Sem _proSem;
  
  // mutex with con
  pthread_mutex_t _conMutex;
  // mutex with pro
  pthread_mutex_t _proMutex;
};
